//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/xml/cnode.h>
#include <buola/xml/lookup.h>
#include <buola/algorithm/split.h>
#include <buola/algorithm/join.h>

namespace buola { namespace xml {

std::string CNode::Lang() const
{
    throw XNotImplemented("xml::CNode::Lang");
}
    
std::string CNode::Content() const
{
    std::string lString;
    for(auto &c : mNode->mChildren)
    {
        if(c.mType==ENodeType::DATA)
            lString+=c.mValue;
    }
    return lString;
}
    
bool CNode::MatchesName(const std::string &pName) const
{
    return (iequals(pName,Name())&&mNode->mType==ENodeType::ELEMENT);
}

CNode CNode::FirstChild(const string_view &pName,bool pIgnoreCase) const
{
    for(auto &c : mNode->mChildren)
    {
        if(pIgnoreCase)
        {
            if(iequals(pName,c.mName))
                return &c;
        }
        else
        {
            if(equals(pName,c.mName))
                return &c;
        }
    }
    
    return nullptr;
}

///\todo next sibling, which requires an iterator to self
///\todo access to attributes

void CNode::SetType(ENodeType pType) const
{
    mNode->mType=pType;
}

///\return a CNode pointing to the created child
CNode CNode::NewChild(const std::string &pName,const std::string &pContent) const
{
    CDoc::SNode *lNewNode=mNode->mDoc->CreateNode(ENodeType::ELEMENT,pName,pContent);
    mNode->mChildren.push_back(*lNewNode);
    return lNewNode;
}

void CNode::Prepend(const CNode &pChild) const
{
    mNode->mChildren.push_front(*pChild.mNode);
}

void CNode::Append(const CNode &pChild) const
{
    mNode->mChildren.push_back(*pChild.mNode);
}

void CNode::Insert(const CNode &pWhere,const CNode &pChild) const
{
    mNode->mChildren.insert(mNode->mChildren.iterator_to(*pWhere.mNode),*pChild.mNode);
}

void CNode::RemoveFirstChild() const
{
    mNode->mChildren.pop_front();
}

void CNode::RemoveLastChild() const
{
    mNode->mChildren.pop_back();
}

void CNode::RemoveChild(const CNode &pChild) const
{
    mNode->mChildren.erase(mNode->mChildren.iterator_to(*pChild.mNode));
}

void CNode::ClearChildren() const
{
    mNode->mChildren.clear();
}

void CNode::ClearAttributes() const
{
    mNode->mAttributes.clear();
}

bool CNode::GetAttrib(const std::string &pName,std::string &pReturn) const
{
    for(auto &a : mNode->mAttributes)
    {
        if(equals(pName,a.mName))
        {
            pReturn=std::string(a.mValue);
            return true;
        }
    }
    
    return false;
}

std::string CNode::GetStringAttrib(const std::string &pName,const std::string &pDefault)
{
    std::string lString;

    if(!GetAttrib(pName,lString)) return pDefault;

    return lString;
}

std::wstring CNode::GetWStringAttrib(const std::string &pName,const std::wstring &pDefault)
{
    std::string lString;

    if(!GetAttrib(pName,lString)) return pDefault;

    return utf32(lString);
}

bool CNode::GetBoolAttrib(const std::string &pName,bool pDefault)
{
    std::string lString;

    if(!GetAttrib(pName,lString)) return pDefault;

    return to_<bool>(lString);
}

int CNode::GetIntAttrib(const std::string &pName,int pDefault)
{
    std::string lString;

    if(!GetAttrib(pName,lString)) return pDefault;

    return to_<int>(lString);
}

double CNode::GetDoubleAttrib(const std::string &pName,double pDefault)
{
    std::string lString;

    if(!GetAttrib(pName,lString)) return pDefault;

    return to_<double>(lString);
}

void CNode::GetListAttrib(const std::string &pName,std::vector<std::wstring> &pList)
{
    std::string lString;

    pList.clear();

    if(!GetAttrib(pName,lString)) return;

    std::wstring lWString=utf32(lString);
    split(lWString,pList,fn::equal_to(','));
}

void CNode::GetListAttrib(const std::string &pName,std::vector<std::string> &pList)
{
    std::string lString;

    pList.clear();

    if(!GetAttrib(pName,lString)) return;

    split(lString,pList,fn::equal_to(','));
}

void CNode::SetAttrib(const std::string &pName,const std::string &pValue) const
{
    for(auto &a : mNode->mAttributes)
    {
        if(equals(pName,a.mName))
        {
            a.mValue=mNode->mDoc->CreateString(pValue);
            return;
        }
    }
    
    mNode->mAttributes.push_front(*mNode->mDoc->CreateAttribute(pName,pValue));
}
    
void CNode::SetStringAttrib(const std::string &pName,const std::wstring &pString)
{
    SetAttrib(pName,utf8(pString));
}

void CNode::SetIntAttrib(const std::string &pName,int pValue)
{
    SetAttrib(pName,to_string(pValue));
}

void CNode::SetDoubleAttrib(const std::string &pName,double pValue)
{
    SetAttrib(pName,to_string(pValue));
}

void CNode::SetBoolAttrib(const std::string &pName,bool pValue)
{
    SetAttrib(pName,pValue?"true":"false");
}

void CNode::SetListAttrib(const std::string &pName,const std::vector<std::wstring> &pList)
{
    SetStringAttrib(pName,join(pList,","));
}

void CNode::SetListAttrib(const std::string &pName,const std::vector<std::string> &pList)
{
    SetAttrib(pName,join(pList,","));
}

std::string CNode::ToString() const
{
    std::string lStr;
    AppendTo(lStr);
    return lStr;
}

void CNode::AppendTo(std::string &pString) const
{
    if(mNode->mType==ENodeType::DOCUMENT)
    {
        for(auto &c : mNode->mChildren)
            CNode(&c).AppendTo(pString);
    }
    else if(mNode->mType==ENodeType::ELEMENT)
    {
        pString+='<';
        pString+=mNode->mName;
        for(const auto &a : mNode->mAttributes)
        {
            pString+=' ';
            pString+=a.mName;
            pString+="=\"";
            pString+=a.mValue;
            pString+='"';
        }
        
        if(mNode->mChildren.empty()&&mNode->mValue.empty())
        {
            pString+="/>";
        }
        else
        {
            pString+='>';
            pString+=mNode->mValue;
            for(auto &c : mNode->mChildren)
                CNode(&c).AppendTo(pString);
            pString+="</";
            pString+=mNode->mName;
            pString+='>';
        }
    }
    else if(mNode->mType==ENodeType::DATA)
    {
        pString+=mNode->mValue;
    }
    else if(mNode->mType==ENodeType::DECLARATION)
    {
        pString+="<?xml";

        for(const auto &a : mNode->mAttributes)
        {
            pString+=' ';
            pString+=a.mName;
            pString+="=\"";
            pString+=a.mValue;
            pString+='"';
        }
        
        pString+="?>\n";
    }
}

/*namespace xml*/ } /*namespace buola*/ }
